#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"


enum
{
  SOCKET_ERROR = 1,
  BIND_ERROR,
  LISTEN_ERROR,
  CONNECT_ERROR
};

const int backlog = 10;
class Socket
{
public:
  // 创建
  void Initsocket()
  {
    _sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (_sockfd < 0)
    {
      lg(Fatal, "sockfd create error, errno: %d. errmsg: %s", errno, strerror(errno));
      exit(SOCKET_ERROR);
    }
  }
  // 绑定
  void Bind(const uint16_t& port)
  {
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = INADDR_ANY;

    socklen_t len = sizeof(local);
    if (bind(_sockfd, (const sockaddr*)&local, len) < 0)
    {
      lg(Fatal, "bind error, errno: %d, errmsg: %s", errno, strerror(errno));
      exit(BIND_ERROR);
    }


  }

  // 监听状态
  void Listen()
  {
    if (listen(_sockfd, backlog) < 0)
    {
      lg(Fatal, "listen error, errno: %d, errmsg: %s", errno, strerror(errno));
      exit(LISTEN_ERROR);
    }

  }

  // 接收
  int Accept(std::string* clientip, uint16_t* clientport)
  {
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    int sockfd = accept(_sockfd, (struct sockaddr*)&client, &len);
    if (sockfd < 0)
    {
      lg(Warning, "accept error ,errno: %d, errmsg: %s", errno, strerror(errno));
      return -1;
    }

    char buffer[4096];
    inet_ntop(AF_INET, &client.sin_addr, buffer, sizeof(buffer));
    *clientip = buffer;
    *clientport = ntohs(client.sin_port);

    return sockfd;
  }

  bool Connect(const std::string& serverip, const uint16_t& serverport)
  {
    struct sockaddr_in peer;
    memset(&peer, 0, sizeof(peer));
    peer.sin_family = AF_INET;
    peer.sin_port = htons(serverport);
    inet_pton(AF_INET, serverip.c_str(), &(peer.sin_addr));
    socklen_t len = sizeof(peer);
    int n = connect(_sockfd, (const sockaddr*)&peer, len);
    if (n < 0)
    {
      lg(Warning, "connect error, errno: %d, errmsg: %s", errno, strerror(errno));
      return false;
    }
    return true;
  }

  int getSockfd()
  {
    return _sockfd;
  }

  void Close()
  {
    close(_sockfd);
  }

private:
  int _sockfd;
};